#include "MonkeySystemObject.h"
#include "MessageCenter.h"
#include "Utils.h"
#include "Message.h"
#include "MonkeyEventCallback.h"
#include <string>


static JSClass object_class = {
    "System", JSCLASS_HAS_PRIVATE,
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};



MonkeySystemObject::MonkeySystemObject()
{
}


//
// Register this object with the parent object.
//
void MonkeySystemObject::RegisterObject(MonkeyContext *ctx, JSObject *parent)
{
	MonkeyObject::RegisterObject(ctx, parent);

	JS_BeginRequest(Context()->jsContext());

	object = JS_DefineObject(Context()->jsContext(), parent, "system", &object_class, NULL, (JSPROP_READONLY | JSPROP_PERMANENT));
	JS_SetPrivate(Context()->jsContext(), object, this);
    JS_DefineFunction(Context()->jsContext(), object, "sleep", MonkeySystemObject::js_sleep, 1, 0);
    JS_DefineFunction(Context()->jsContext(), object, "print", MonkeySystemObject::js_print, 1, 0);
    JS_DefineFunction(Context()->jsContext(), object, "daemon", MonkeySystemObject::js_daemon, 0, 0);
    JS_DefineFunction(Context()->jsContext(), object, "eventcallback", MonkeySystemObject::js_eventcallback, 2, 0);
	
	JS_EndRequest(Context()->jsContext());
}


//
// Sleep (pause) for the specified number of milliseconds. During this sleep time
// any pending callbacks will be triggered.
//
JSBool MonkeySystemObject::js_sleep(JSContext *cx, uintN argc, jsval *vp)
{
	if (argc >= 1)
	{
		int ms = 1;
		struct timeval target, tp;
		
		JS_ValueToInt32(cx, JS_ARGV(cx, vp)[0], &ms);
		gettimeofday(&target, NULL);
		timeval_addms(&target, ms);
		
		//
		// Loop until we have passed our target wait time.
		//
		gettimeofday(&tp, NULL);
		ms = (int)timeval_diffms(&target, &tp);
		do
		{
			//
			// Wait for any messages for up to the desired wait time remaining.
			//
			MessageCenter::SharedMessageCenter().WaitForMessage(ms);
			
			//
			// Determine how much wait time we have left.
			//
			gettimeofday(&tp, NULL);
			ms = (int)timeval_diffms(&target, &tp);
		} while (ms > 0);

		return JS_TRUE;
	}
	
	return JS_FALSE;
}


//
// Print the given argument to the debug console.
//
JSBool MonkeySystemObject::js_print(JSContext *cx, uintN argc, jsval *vp)
{
	if (argc >= 1)
	{
		const char *string;
		
		string = JS_EncodeString(cx, JS_ValueToString(cx, JS_ARGV(cx, vp)[0]));
		printf("JS: %s\r\n", string);

		return JS_TRUE;
	}
	
	return JS_FALSE;
}


//
// Turn into a daemon and wait for events.
//
JSBool MonkeySystemObject::js_daemon(JSContext *cx, uintN argc, jsval *vp)
{
	for (;;)
	{
		MessageCenter::SharedMessageCenter().WaitForMessage();
		JS_MaybeGC(cx);
	}

	return JS_TRUE;
}


//
// An event callback should be triggered. Call the javascript function.
//
void MonkeySystemObject::jscb_event(Message *msg, void *userInfo)
{
	MonkeyEventCallback *cb = (MonkeyEventCallback *)userInfo;
	JSContext *cx = cb->getContext();
	jsval rval, args[1];


	JS_BeginRequest(cx);
	args[0] = STRING_TO_JSVAL(JS_NewStringCopyN(cx, cb->getEventName().c_str(), cb->getEventName().size()));
	JS_CallFunctionValue(cx, JS_GetGlobalObject(cx), cb->getCallback(), 1, args, &rval);
	JS_EndRequest(cx);
}


//
// Register to receive a callback event when the given notification happens.
//
JSBool MonkeySystemObject::js_eventcallback(JSContext *cx, uintN argc, jsval *vp)
{
	if (argc == 2 && JS_TypeOfValue(cx, JS_ARGV(cx, vp)[1]) == JSTYPE_FUNCTION)
	{
		MonkeyEventCallback *cb;
		const char *name;
		jsval func;

		name = JS_EncodeString(cx, JS_ValueToString(cx, JS_ARGV(cx, vp)[0]));
		JS_ConvertValue(cx, JS_ARGV(cx, vp)[1], JSTYPE_FUNCTION, &func);
		cb = new MonkeyEventCallback(std::string(name), cx, func);
		MessageCenter::SharedMessageCenter().AddObserver(std::string(name), jscb_event, cb);

		return JS_TRUE;
	}
	
	return JS_FALSE;
}


//
// Set a timer that will execute after a specified period of time.
//
JSBool MonkeySystemObject::js_setTimeout(JSContext *cx, uintN argc, jsval *vp)
{
	return JS_TRUE;
}


//
// Set a timer that will execute repeatedly after each time interval has passed.
//
JSBool MonkeySystemObject::js_setInterval(JSContext *cx, uintN argc, jsval *vp)
{
	return JS_TRUE;
}

